﻿using FightLand_Sever.Model.Net;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using System.Threading;

namespace FightLand_Sever.InGame
{
    enum GameStage
    {
        准备阶段,
        叫分阶段,
        抢地主阶段,
        设置地主阶段,
        出牌阶段,
        接牌阶段,
        结束阶段,
        统计阶段
    }
    class Game
    {
        //活动玩家切换委托
        delegate void ThinkingPlayerChange(Player ply, GameStage stage);
        //有玩家出牌时的委托
        delegate void PlayerOutPoker(Player ply, NetPoker[] pks);

        /// <summary>
        /// 游戏结束
        /// </summary>
        public event EventHandler GameEnd;
        /// <summary>
        /// 游戏ID
        /// </summary>
        public long GameID { get; private set; }
        /// <summary>
        /// 指示玩家是否已到齐
        /// </summary>
        public bool Dodge { get; set; }
        /// <summary>
        /// 当前局内倍数
        /// </summary>
        public int Multiple
        {
            get
            {
                return this.multiple;
            }
            set
            {
                if (value == this.multiple) return;
                this.multiple = value;
                //更新倍数信息
                this.SendBase(GameOrderType.倍数更新, "", null, value.ToString());
            }
        }
        /// <summary>
        /// 当前思考的玩家
        /// </summary>
        public Player NowThinkPly { get { return nowThinkPly; } }
        /// <summary>
        /// 上次的出牌玩家
        /// </summary>
        public Player LastSendPly { get { return LastSendPly; } }
        /// <summary>
        /// 标识是否为抢地主模式，否则为叫分模式
        /// </summary>
        public bool RobLand { get { return robland; } }


        Player p1 = null;
        Player p2 = null;
        Player p3 = null;
        Player landPlayer = null; //地主玩家
        Player lastSendPly = null;//上次出牌的玩家
        Player nowThinkPly = null;//当前出牌玩家
        Player[] farmers = new Player[2]; //农民玩家
        Clock clock = new Clock(30); //计时器
        PokerHeap pokerHeap;
        NetPoker[] lastOutPoker = { }; //上次出牌的牌组
        GameStage gameStage = GameStage.准备阶段;
        int btScore = 1; //当局底分
        int multiple = 1; //当局倍数
        int readyCount = 0;  //已经准备的游戏玩家数
        bool started = false; //标识是否已正式进入游戏阶段
        bool isend = false;//标识游戏是否已结束
        public bool robland = true;//标识是否为抢地主模式，否则为叫分模式
        //叫分
        Player maxScorePly = null; //当前叫分分值最大玩家,即地主玩家
        int lun = 0; //叫分或者抢地主轮数
        int maxScore = 0; //当前最大叫分
        //抢地主
        Player firstRob = null;
        Player lastRob = null;
        bool firstisRob = false;

        //记录上次发出的关键指令数据，用于玩家重连
        //NetInfoBase lastjiaofen = null;
        //NetInfoBase lastsendpk = null;
        //NetInfoBase lastMultiple = null;
        //NetInfoBase lastSetland = null;

        public Game(Player p1, Player p2, Player p3, int btScore, long id = -1)
        {
            this.GameID = id > 0 ? (id++) : id;
            this.Dodge = false;
            this.clock.TikChange += Clock_TikChange;
            this.clock.Stopped += Clock_Stopped;
            this.clock.OverTime += Clock_OverTime;
            this.btScore = btScore;
            this.p1 = p1;
            this.p2 = p2;
            this.p3 = p3;
            p1.GameData.OnGameData += GameSokReceive;
            p2.GameData.OnGameData += GameSokReceive;
            p3.GameData.OnGameData += GameSokReceive;
            p1.RoomData.RoomSokConnect += PlayerReconnect;
            p1.RoomData.RoomSokDisconnect += PlayerDisConnect;
            p2.RoomData.RoomSokConnect += PlayerReconnect;
            p2.RoomData.RoomSokDisconnect += PlayerDisConnect;
            p3.RoomData.RoomSokConnect += PlayerReconnect;
            p3.RoomData.RoomSokDisconnect += PlayerDisConnect;
        }

        //玩家重连时触发
        private void PlayerReconnect(Player ply)
        {
            //暂未开发
        }

        //玩家断开连接时触发
        private void PlayerDisConnect(Player ply)
        {
            if (isend) return;
            var data = QuitPlayerPunish(ply);
            this.SendBase(GameOrderType.玩家退出, data);
            GameOver();
        }

        //收到玩家局内数据时触发
        private void GameSokReceive(Player ply, NetInfoBase info)
        {
            switch ((GameOrderType)info.OrderType)
            {
                case GameOrderType.有玩家叫分:
                    {
                        int score = int.Parse(info.Tag);
                        this.SomeoneJiaofen(ply, score);
                        break;
                    }
                case GameOrderType.出牌:
                    {
                        var p = JsonConvert.DeserializeAnonymousType(info.JsonData, new { pks = new NetPoker[0], type = OutPokerType.Error_Card });
                        OutPokerType ty = p.type;
                        if (ty == OutPokerType.炸弹 || ty == OutPokerType.王炸) this.Multiple *= 2;
                        this.SomeoneOutPoker(ply, p.pks);
                        break;
                    }
                case GameOrderType.玩家已准备:
                    {
                        this.readyCount++;
                        if (readyCount >= 3 && !started) AllPlayerReady();
                        break;
                    }
                case GameOrderType.有玩家抢地主:
                    {
                        var isrob = bool.Parse(info.Tag);
                        this.SomeoneRob(ply, isrob);
                        break;
                    }
            }
        }

        //超时触发
        private void Clock_OverTime(Clock clock)
        {
            var ply = clock.TagPlayer;
            switch (this.gameStage)
            {
                case GameStage.叫分阶段:
                    this.SomeoneJiaofen(ply, 0);
                    break;
                case GameStage.抢地主阶段:
                    this.SomeoneRob(ply, false);
                    break;
                case GameStage.出牌阶段:
                    var pks = new NetPoker[] { clock.TagPlayer.GameData.HandPks[0] };
                    ply.GameData.HandPks.RemoveAt(0);
                    this.SomeoneOutPoker(ply, pks);
                    break;
                case GameStage.接牌阶段:
                    this.SomeoneOutPoker(ply, new NetPoker[0]);
                    break;
            }
        }

        //计时器被中止时触发
        private void Clock_Stopped(Clock sender)
        {

        }

        //计时变更时触发
        private void Clock_TikChange(Clock sender, int nowtik)
        {
            this.SendBase(GameOrderType.计时滴答, "", null, nowtik.ToString());
        }

        //玩家全部准备后调用
        private void AllPlayerReady()
        {
            this.started = true;
            this.pokerHeap = new PokerHeap();
            //洗牌
            this.pokerHeap.Shuffle();
            //获取随机后的牌堆(51张,不包括地主牌)
            var hep = this.pokerHeap.randHeap;
            //分别给出17张
            var h1 = hep.Take(17).ToArray();
            var h2 = hep.Skip(17).Take(17).ToArray();
            var h3 = hep.Skip(34).ToArray();
            //分别添加到他们的手牌
            this.p1.GameData.AddHandPk(h1);
            this.p2.GameData.AddHandPk(h2);
            this.p3.GameData.AddHandPk(h3);
            //生成手牌json
            var p1h = JsonConvert.SerializeObject(h1);
            var p2h = JsonConvert.SerializeObject(h2);
            var p3h = JsonConvert.SerializeObject(h3);
            //发出手牌
            this.SendBase(GameOrderType.发我方手牌, p1h, this.p1);
            this.SendBase(GameOrderType.发我方手牌, p2h, this.p2);
            this.SendBase(GameOrderType.发我方手牌, p3h, this.p3);

            int ran = new Random().Next(0, 3);
            Player p = ran == 0 ? this.p1 : ran == 1 ? p2 : p3; //随机玩家开始叫分
            this.nowThinkPly = p;
            this.clock.Start();
            clock.TagPlayer = p;
            if (robland)
            {
                this.firstRob = p;
                this.gameStage = GameStage.抢地主阶段;
                this.SendBase(GameOrderType.指定玩家抢地主, null, null, p.PlayerID);
            }
            else
            {
                string json = JsonConvert.SerializeObject(new
                {
                    p.PlayerID,
                    Min = 1 //指定最小叫分分值
                });
                this.gameStage = GameStage.叫分阶段;
                this.SendBase(GameOrderType.指定玩家叫分, json, null);
            }
        }

        //有玩家叫分处理函数
        private void SomeoneJiaofen(Player ply, int score)
        {
            lun++;
            //判断当前叫分分值最高的玩家
            if (this.maxScore < score)
            {
                this.maxScore = score; //重置最大分
                this.maxScorePly = ply;//设置当前最大分玩家
                this.Multiple = this.maxScore;//设置倍数为当前叫分分值
            }
            string json = JsonConvert.SerializeObject(new { ply.PlayerID, Score = score });
            this.SendBase(GameOrderType.有玩家叫分, json, ply.LastPlayer);
            this.SendBase(GameOrderType.有玩家叫分, json, ply.NextPlayer);
            //判断叫分阶段是否应该结束
            if (this.maxScore == 3 || (this.lun == 3 && this.maxScore > 0))
            {
                this.landPlayer = maxScorePly;
                this.clock.Stop();
                this.JiaoFenOrRobOver();
                return;
            }
            if (lun == 3 && this.maxScore == 0)
            {
                this.clock.Stop();
                this.ReStart();
                return;
            }
            //如果不满足套件则继续指定下个玩家叫分,并指定最小叫分值
            json = JsonConvert.SerializeObject(new { ply.NextPlayer.PlayerID, Min = maxScore + 1 });
            this.nowThinkPly = ply.NextPlayer;
            this.SendBase(GameOrderType.指定玩家叫分, json, null);
            //重新开始计时
            clock.TagPlayer = ply.NextPlayer;
            clock.ReStart();
        }

        //有玩家抢地主
        private void SomeoneRob(Player ply, bool rob)
        {
            lun++;
            var json = JsonConvert.SerializeObject(new
            {
                pid = ply.PlayerID,
                rob
            });
            this.SendBase(GameOrderType.有玩家抢地主, json, ply.LastPlayer);
            this.SendBase(GameOrderType.有玩家抢地主, json, ply.NextPlayer);
            if (lun == 1) this.firstisRob = rob; //记录初始玩家是否抢地主
            if (rob)
            {
                this.Multiple *= 2;
                this.lastRob = ply;
                //第三个玩家抢地主后判断第一个玩家是否可以继续抢
                if (lun == 3) if(!firstisRob) landPlayer = ply;
                if (lun >= 4) landPlayer = ply;
            }
            else
            { 
                if (lun==3)
                {
                    //如果一轮下来没有人抢地主则重开
                    if (lastRob == null)
                    {
                        clock.Stop();
                        ReStart();
                        return;
                    }
                    //如果上个抢地主的玩家时开始的玩家,则直接赋予地主
                    if (firstRob==lastRob)
                    {
                        landPlayer = firstRob;
                    }
                }
                if (lun >= 4)
                {
                    if (lastRob != null) landPlayer = lastRob;
                }
            }
            if (landPlayer != null)
            {
                clock.Stop();
                this.JiaoFenOrRobOver();
                return;
            }
            this.SendBase(GameOrderType.指定玩家抢地主, null, null, ply.NextPlayer.PlayerID);
            clock.TagPlayer = ply.NextPlayer;
            clock.ReStart();
        }

        //叫分或抢地主结束时调用
        private void JiaoFenOrRobOver()
        {
            this.gameStage = GameStage.设置地主阶段;
            //给出地主牌信息
            string json = JsonConvert.SerializeObject(new { this.landPlayer.PlayerID, LandPks = this.pokerHeap.landPks });
            this.landPlayer.GameData.AddHandPk(this.pokerHeap.landPks.ToArray());
            //添加农民玩家
            this.farmers[0] = this.landPlayer.LastPlayer;
            this.farmers[1] = this.landPlayer.NextPlayer;
            this.nowThinkPly = this.landPlayer;
            this.SendBase(GameOrderType.设置地主, json);
            //设置完地主后准备进入出牌阶段,接收出牌信息
            clock.TagPlayer = this.landPlayer;//将地主绑定到闹钟
            this.clock.Start(3);
            this.gameStage = GameStage.出牌阶段;
        }

        //有玩家出牌处理函数
        private void SomeoneOutPoker(Player ply, NetPoker[] pks)
        {
            if (pks == null) pks = new NetPoker[0];
            //向其他玩家给出出牌信息
            string json = JsonConvert.SerializeObject(new { ply.PlayerID, OutPks = pks });
            this.SendBase(GameOrderType.出牌, json, ply.LastPlayer);
            this.SendBase(GameOrderType.出牌, json, ply.NextPlayer);
            //在服务器端删除扑克
            ply.GameData.RemoveHandPk(pks);
            if (ply.GameData.HandPks.Count == 0)
            {
                this.gameStage = GameStage.结束阶段;
                this.SomeoneWin(ply);
                return;
            }
            //设置当前思考玩家
            this.nowThinkPly = ply.NextPlayer;
            //如果出牌了，则将其设置为上一个出牌的玩家
            if (pks.Length > 0) this.lastSendPly = ply;
            //如果上一个出牌的玩家是当前出牌玩家的下一家，则下一个玩家进入出牌阶段
            if (lastSendPly == ply.NextPlayer)
            {
                this.gameStage = GameStage.出牌阶段;
            }
            //否则下一个玩家还得继续接牌
            else
            {
                this.gameStage = GameStage.接牌阶段;
            }
            //重新计时,绑定下家
            this.clock.TagPlayer = ply.NextPlayer;
            this.clock.ReStart();
        }

        //当有玩家胜利时调用
        private void SomeoneWin(Player win)
        {
            //发送胜利信息
            var obj = JsonConvert.SerializeObject(new
            {
                Ply1ID = this.p1.PlayerID,
                Ply1Pks = this.p1.GameData.HandPks,
                Ply2ID = this.p3.PlayerID,
                Ply2Pks = this.p3.GameData.HandPks,
                Ply3ID = this.p2.PlayerID,
                Ply3Pks = this.p2.GameData.HandPks,
                WinPlyID = win.PlayerID
            });
            this.SendBase(GameOrderType.有玩家胜出, obj);
            //结算玩家对局分数
            ScoreCalculate(win == landPlayer);
            GameOver();
        }

        //分数结算
        private void ScoreCalculate(bool landwin)
        {
            Player win = landPlayer;
            if (!landwin) win = win.LastPlayer;
            var sumScore = (long)this.multiple * this.btScore;
            //如果胜利玩家是地主
            if (landPlayer == win)
            {
                win.Mark += (sumScore * 2);
                win.LastPlayer.Mark -= sumScore;
                win.NextPlayer.Mark -= sumScore;
            }
            else
            {
                win.Mark += sumScore;
                if (landPlayer == win.LastPlayer)
                {
                    win.LastPlayer.Mark -= (sumScore * 2);
                    win.NextPlayer.Mark += sumScore;
                }
                else
                {
                    win.LastPlayer.Mark += sumScore;
                    win.NextPlayer.Mark -= (sumScore * 2);
                }
            }
        }

        //退出玩家制裁
        private string QuitPlayerPunish(Player ply)
        {
            ply.Mark -= this.Multiple * this.btScore;
            ply.LastPlayer.Mark += 300;
            ply.NextPlayer.Mark += 300;
            return JsonConvert.SerializeObject(new
            {
                quitpid = ply.PlayerID,
                deduct = this.Multiple * this.btScore,
                rew = 300
            });
        }

        //游戏结束
        private void GameOver()
        {
            if (this.isend) return;
            this.isend = true;
            this.gameStage = GameStage.结束阶段;
            this.clock.Del();
            if (this.GameEnd != null) this.GameEnd(this, new EventArgs());
            Log.Print(this.GameID + " 游戏结束", ConsoleColor.Red);
        }

        /// <summary>
        /// 重新发牌
        /// </summary>
        private void ReStart()
        {
            this.landPlayer = null;
            this.firstRob = null;
            this.lastRob = null;
            this.lun = 0;
            this.maxScore = 0;
            this.maxScorePly = null;
            this.Multiple = 1;
            this.AllPlayerReady();
        }

        //发送数据给局内玩家
        private void SendBase(GameOrderType order, string json = "", Player p = null, string tag = null)
        {
            if (p == null)
            {
                p1.GameData.SendData(json, order, tag);
                p1.GameData.SendLastData(json, order, tag);
                p1.GameData.SendNextData(json, order, tag);
            }
            else
            {
                p.GameData.SendData(json, order, tag);
            }
            NetInfoBase bas = new NetInfoBase((int)order, json, tag);
        }
    }
}
